using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace JTLeigh.Tools.Development.CopySourceAsHtml.UnitTests
{

    /// <summary>
    /// Unit tests for the <see cref="HtmlBlock"/> class.
    /// </summary>
    [TestClass]
    public class HtmlBlockFixture
    {
        #region Methods

        #region Constructors

        /// <summary>
        /// Ensures the <see cref="HtmlBlock"/> constructor sets the default foreground color, 
        /// background color, and bold state.
        /// </summary>
        [TestMethod]
        public void TestConstructorSetsDefaults()
        {
            HtmlBlock block;

            block = new HtmlBlock();
            Assert.AreEqual(0, block.ForegroundColor);
            Assert.AreEqual(0, block.BackgroundColor);
            Assert.IsFalse(block.Bold);
        }

        /// <summary>
        /// Ensures the <see cref="HtmlBlock"/> constructor correctly throws <see cref="ArgumentNullException"/>
        /// when called with a <see langword="null"/> parent block.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void TestConstructorWithNullParentBlock()
        {
            new HtmlBlock(null);
        }

        /// <summary>
        /// Ensures the <see cref="HtmlBlock"/> constructor inherits the foreground color, 
        /// background color, and bold state from the supplied parent block.
        /// </summary>
        [TestMethod]
        public void TestConstructorInheritsFromParentBlock()
        {
            HtmlBlock parentBlock;
            HtmlBlock block;

            parentBlock = new HtmlBlock();
            parentBlock.ForegroundColor = 1;
            parentBlock.BackgroundColor = 2;
            parentBlock.Bold = true;

            block = new HtmlBlock(parentBlock);

            Assert.AreEqual(parentBlock.ForegroundColor, block.ForegroundColor);
            Assert.AreEqual(parentBlock.BackgroundColor, block.BackgroundColor);
            Assert.AreEqual(parentBlock.Bold, block.Bold);
        }

        #endregion // Constructors

        #region Append

        /// <summary>
        /// Ensures <see cref="HtmlBlock.Append"/> appends content to the block and updates the
        /// <see cref="HtmlBlock.LeadingSpaces"/>, <see cref="HtmlBlock.TrailingSpaces"/>, and
        /// <see cref="HtmlBlock.HasContent"/> properties, and that the resulting HTML renders
        /// correctly.
        /// </summary>
        [TestMethod]
        public void TestAppend()
        {
            HtmlBlock block;

            block = new HtmlBlock();

            Assert.IsFalse(block.HasContent);
            Assert.AreEqual(0, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("", block.GetHtml(true, 0, 4));

            block.Append(' ', 4);
            Assert.IsFalse(block.HasContent);
            Assert.AreEqual(1, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual(" ", block.GetHtml(true, 0, 4));

            block.Append(' ', 4);
            Assert.IsFalse(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; ", block.GetHtml(true, 0, 4));

            block.Append('T', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; T", block.GetHtml(true, 0, 4));

            block.Append(' ', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(1, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; T ", block.GetHtml(true, 0, 4));

            block.Append('T', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; T T", block.GetHtml(true, 0, 4));

            block.Append(' ', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(1, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; T T ", block.GetHtml(true, 0, 4));

            block.Append(' ', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(2, block.LeadingSpaces);
            Assert.AreEqual(2, block.TrailingSpaces);
            Assert.AreEqual("&nbsp; T T&nbsp; ", block.GetHtml(true, 0, 4));
        }

        /// <summary>
        /// Ensures <see cref="HtmlBlock.Append"/> encodes encodable characters before appending 
        /// them to the block.
        /// </summary>
        [TestMethod]
        public void TestAppendEncodableCharacters()
        {
            HtmlBlock block;

            block = new HtmlBlock();

            Assert.IsFalse(block.HasContent);
            Assert.AreEqual(0, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("", block.GetHtml(true, 0, 4));

            block.Append('<', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(0, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&lt;", block.GetHtml(true, 0, 4));

            block.Append('e', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(0, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&lt;e", block.GetHtml(true, 0, 4));

            block.Append('>', 4);
            Assert.IsTrue(block.HasContent);
            Assert.AreEqual(0, block.LeadingSpaces);
            Assert.AreEqual(0, block.TrailingSpaces);
            Assert.AreEqual("&lt;e&gt;", block.GetHtml(true, 0, 4));
        }

        #endregion // Append

        #region GetWhiteSpace

        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns <see cref="String.Empty"/> when
        /// called with to get 0 spaces of whitespace.
        /// </summary>
        [TestMethod]
        public void TestGetWhiteSpaceWithNoSpace()
        {
            Assert.AreEqual(String.Empty, HtmlBlock.GetWhiteSpace(true, 0, 4));
        }

        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns a breaking space when called to
        /// get 1 space of whitespace with leading spaces allowed.
        /// </summary>
        [TestMethod]
        public void TestGetWhiteSpaceWith1SpaceWithLeadingSpaceAllowed()
        {
            Assert.AreEqual(" ", HtmlBlock.GetWhiteSpace(true, 1, 4));
        }

        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns a non-breaking space when called 
        /// to get 1 space of whitespace with leading spaces not allowed.
        /// </summary>
        [TestMethod]
        public void TestGetWhiteSpaceWith1SpaceWithLeadingSpaceNotAllowed()
        {
            Assert.AreEqual("&nbsp;", HtmlBlock.GetWhiteSpace(false, 1, 4));
        }

        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns the correct patterns of breaking
        /// and non-breaking spaces when called to get varying amounts of whitespace with varying
        /// tab sizes.
        /// </summary>
        [TestMethod]
        public void TestGetWhiteSpace()
        {
            // NOTE: See the other TestGetWhiteSpace classes for some special cases, like when
            // tabsize=1 or when spaces=(n*tabsize)+1.

            // tabsize=4
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 4, 4));
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 6, 4));
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 7, 4));
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 8, 4));

            // tabsize=2
            Assert.AreEqual("&nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 4, 2));
            Assert.AreEqual("&nbsp; &nbsp; &nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 8, 2));

            // tabsize=3
            Assert.AreEqual("&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 8, 3));
        }

        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns the correct patterns of breaking
        /// and non-breaking spaces when called to get <c>(n * tabsize) + 1</c> characters of 
        /// whitespace.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is a special case where we were originally getting any two breaking spaces in a 
        /// row.  We've now changed the logic that appends the final breaking space so that it 
        /// checks the previous space and, if it's a breaking space, replaces it with a 
        /// non-breaking space.
        /// </para>
        /// </remarks>
        [TestMethod]
        public void TestGetWhiteSpaceWithNTabSizePlus1Spaces()
        {
            // tabsize=3
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 4, 3));
            Assert.AreEqual("&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 7, 3));

            // tabsize=4
            Assert.AreEqual("&nbsp;&nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 5, 4));
            Assert.AreEqual("&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; ", HtmlBlock.GetWhiteSpace(true, 9, 4));
        }
        
        /// <summary>
        /// Ensures <see cref="HtmlBlock.GetWhiteSpace"/> returns the correct patterns of breaking
        /// and non-breaking spaces when called to get varying amounts of whitespace with 
        /// tabsize=1.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is a special case where we were originally getting any number of breaking spaces in 
        /// a row.  We've now special-cased tabsize=1 so that it's treated as if it were 
        /// tabsize=2.
        /// </para>
        /// </remarks>
        [TestMethod]
        public void TestGetWhiteSpaceWithTabSize1()
        {
            // tabsize=1
            Assert.AreEqual("&nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 4, 1));
            Assert.AreEqual("&nbsp; &nbsp; &nbsp; &nbsp; ", HtmlBlock.GetWhiteSpace(true, 8, 1));
        }

        #endregion // GetWhiteSpace

        #endregion // Methods
    }

}
